package com.autumn.util.reflect;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.List;
import java.util.Map;

import com.autumn.util.ArgumentException;
import com.autumn.util.ExceptionUtils;
import com.esotericsoftware.reflectasm.MethodAccess;

/**
 * Bean属性
 * 
 * @author 杨昌国
 *
 *         2017-09-27 17:03:06
 */
public class BeanProperty {

	private final String name;
	private final Field field;
	private final Method setter;
	private final Method getter;
	private final MethodAccess methodAccess;
	private final Class<?> type;
	private final Class<?> beanClass;
	private final int setterIndex;
	private final int getterIndex;
	private final boolean canFieldWrite;
	private final Type[] genericParameterTypes;

	/**
	 * 实例化
	 * 
	 * @param field
	 *            字段
	 * @throws IntrospectionException
	 */
	public BeanProperty(Field field) throws IntrospectionException {
		this(ExceptionUtils.checkNotNull(field, "field").getDeclaringClass(), field,
				new PropertyDescriptor(ExceptionUtils.checkNotNull(field, "field").getName(),
						ExceptionUtils.checkNotNull(field, "field").getDeclaringClass()));
	}

	/**
	 * 实例化
	 * 
	 * @param beanClass
	 *            Bean类型
	 * @param propertyDescriptor
	 *            属性
	 */
	public BeanProperty(Class<?> beanClass, PropertyDescriptor propertyDescriptor) {
		this(beanClass, null, propertyDescriptor);
	}

	/**
	 * 实例化
	 * 
	 * @param beanClass
	 *            Bean 类型
	 * @param field
	 * @param propertyDescriptor
	 */
	public BeanProperty(Class<?> beanClass, Field field, PropertyDescriptor propertyDescriptor) {
		this.beanClass = ExceptionUtils.checkNotNull(beanClass, "beanClass");
		this.field = field;
		ExceptionUtils.checkNotNull(propertyDescriptor, "propertyDescriptor");
		this.name = propertyDescriptor.getName();
		this.getter = propertyDescriptor.getReadMethod();
		if (this.getter == null) {
			throw new ArgumentException("get 方法不能为null。");
		}
		Type genericType = this.getter.getGenericReturnType();
		if (genericType != null && genericType instanceof ParameterizedType) {
			ParameterizedType pt = (ParameterizedType) genericType;
			this.genericParameterTypes = pt.getActualTypeArguments();

		} else {
			this.genericParameterTypes = new Type[0];
		}
		this.setter = propertyDescriptor.getWriteMethod();
		if (genericType instanceof TypeVariable) {
			Map<String, Class<?>> typeMap = ReflectUtils.getGenericActualArgumentsTypeMap(beanClass);
			Class<?> varType = typeMap.get(((TypeVariable<?>) genericType).getName());
			if (varType != null) {
				this.type = varType;
			} else {
				this.type = this.getter.getReturnType();
			}
		} else {
			this.type = this.getter.getReturnType();
		}
		methodAccess = ReflectUtils.getMethodAccess(this.beanClass);
		getterIndex = methodAccess.getIndex(this.getter.getName(), 0);
		this.canFieldWrite = this.field != null && !Modifier.isFinal(field.getModifiers());
		if (this.setter != null) {
			setterIndex = methodAccess.getIndex(this.setter.getName(), this.getter.getReturnType());
		} else {
			setterIndex = -1;
			if (this.canFieldWrite) {
				this.field.setAccessible(true);
			}
		}
	}

	/**
	 * 获取Bean类型
	 * 
	 * @return
	 */
	public Class<?> getBeanClass() {
		return beanClass;
	}

	/**
	 * 获取值
	 * 
	 * @param instance
	 *            实例
	 * @return
	 * @author 杨昌国 2017-09-29 10:00:35
	 */
	public Object getValue(Object instance) {
		return methodAccess.invoke(instance, getterIndex);
	}

	/**
	 * 设置值
	 * 
	 * @param instance
	 *            实例
	 * @param value
	 *            值
	 * @author 杨昌国 2017-09-29 10:01:46
	 */
	public void setValue(Object instance, Object value) {
		if (this.setter != null) {
			methodAccess.invoke(instance, setterIndex, value);
		} else if (this.canFieldWrite) {
			try {
				this.field.set(instance, value);
			} catch (IllegalArgumentException e) {
				ExceptionUtils.throwSystemException(e.getMessage(), e);
			} catch (IllegalAccessException e) {
				ExceptionUtils.throwSystemException(e.getMessage(), e);
			}
		} else {
			ExceptionUtils.throwNotSupportException("set 方法与 field 均为 null 或 field 不支持写,不支持的操作。");
		}
	}

	/**
	 * 是否有注解
	 * 
	 * @param annotationClass
	 * @return
	 * @author 杨昌国 2017-09-27 17:14:29
	 */
	public boolean isAnnotation(Class<? extends Annotation> annotationClass) {
		return getAnnotation(annotationClass) != null;
	}

	/**
	 * 获取指定的注解
	 *
	 * @param annotationClass
	 * @param <T>
	 * @return
	 */
	public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
		T result = null;
		if (getter != null) {
			result = getter.getAnnotation(annotationClass);
		}
		if (result == null && setter != null) {
			result = setter.getAnnotation(annotationClass);
		}
		if (result == null && field != null) {
			result = field.getAnnotation(annotationClass);
		}
		return result;
	}

	/**
	 * 获取名称
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:12:21
	 */
	public String getName() {
		return name;
	}

	/**
	 * 获取字段
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:12:27
	 */
	public Field getField() {
		return field;
	}

	/**
	 * 获取 get Method
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:12:36
	 */
	public Method getSetter() {
		return setter;
	}

	/**
	 * 获取 set Method
	 * 
	 * @return
	 * @author 杨昌国 2017-09-27 17:12:53
	 */
	public Method getGetter() {
		return getter;
	}

	/**
	 * 获取类型
	 * 
	 * @return
	 * @author 杨昌国 2017-10-10 09:57:56
	 */
	public Class<?> getType() {
		return type;
	}

	/**
	 * 是否是数组
	 * 
	 * @return
	 */
	public boolean isArray() {
		return getType().isArray();
	}

	/**
	 * 是否是接口
	 * 
	 * @return
	 */
	public boolean isInterface() {
		return getType().isInterface();
	}

	/**
	 * 是否是抽象类型
	 * 
	 * @return
	 */
	public boolean isAbstract() {
		return Modifier.isAbstract(getType().getModifiers());
	}

	/**
	 * 是否是泛型类型
	 * 
	 * @return
	 */
	public boolean isGenericType() {
		return getGenericParameterTypes() != null && getGenericParameterTypes().length > 0;
	}

	/**
	 * 是否是 List 类型
	 * 
	 * @return
	 */
	public boolean isListType() {
		return List.class.isAssignableFrom(this.getType());
	}

	/**
	 * 是否是 Map 类型
	 * 
	 * @return
	 */
	public boolean isMapType() {
		return Map.class.isAssignableFrom(this.getType());
	}

	/**
	 * 获取泛型参数类型数组
	 * 
	 * @return
	 */
	public final Type[] getGenericParameterTypes() {
		return genericParameterTypes;
	}

	@Override
	public String toString() {
		return this.getName() + " (" + this.getType() + ")";
	}

}
